在上一篇我們有使用同名函式與 pattern matching 做出函式層級的條件判斷,
當然 Elixir 也是有 if
與 else
的。
if 3 > 2 do
"三大於二"
end
list = [1, 2]
if Enum.any?(list) do
"list 裡面有東西"
end
我們也可以加上 else
your_lucky_number = 5
if Enum.member?([1, 2, 3], your_lucky_number) do
"中獎"
else
"沒中獎"
end
要注意的是 Elixir 沒有 else if
,如果是多項條件的話可以用待會介紹的 case
或 cond
為什麼說是 if 函式 而不是 語法呢?
原來 if/2
是在 Kernel 底下的函式 Kernel.if/2
(註:if 使用了 macro 讓我們用起來能有像其他語言的語法)
因為他是其實是函式,所以我們可以這樣使用
if(3 > 2, do: "三大於二啦")
if(false, do: "真", else: "假")
if/2 就如上面寫的,收兩個變數,第一個是我們要判斷的條件,上面的範例為 false
,接著看起來有 do
與 else
兩個但是其實是一個我們在第二篇提到的 keyword list ,如果 keyword list 是最後一個變數的話,可以省略 []
# 加上以省略的 []
if(false, [do: "真", else: "假"])
# 不用 keyword list 的簡寫
if(false, [{:do, "真"}, {:else, "假"}])
所以通常會怎麼使用呢?
如果判斷與結果情況複雜的時候會寫成多行
if Map.has_key?(%{a: 1}, :a) do
# 很多判斷
# 更多行
"Map 裡面有 :a"
else
nil
end
如果判斷與結果情況簡單的時候會寫成一行
if Map.has_key?(%{a: 1}, :a), do: 1, else: 2
另外常常會有用變數收集 if 結果的情形
message =
if Enum.any?([]) do
"空的"
else
"有東西"
end
message
#=> "空的"
剛剛才講說沒有 else if
,那要怎麼做多項條件的判斷呢? 這時候就可以用 cond
,他會從上到下判斷,直到找到第一個條件為真,並執行 ->
後面的程式區塊
i = 2
cond do
i < 1 -> "小於一"
Integer.is_odd(i) -> "是奇數"
is_atom(i) -> "是 atom"
true -> "其他"
end
#=> "其他"
在上面這個例子,我們一個一個把 i 帶進去 ->
左邊的判斷,第一個變成正向的時候就會執行 ->
右邊的程式區塊
(在 Elixir 裡,if 與 cond 判斷時,只有 false 與 nil 是反向,其他不管是 true, 數值, 空 list, 都是正向)
另外,如果沒有任何一個條件為成立的話,會跳出 CondClauseError
錯誤,如果要這不是預期行為的話,可以在最後加上 true ->
來當作預設值,這樣就算前面全部都失敗,這一個一定會成立。
case 使用了 pattern matching 來做判斷,是非常方便的語法
case %{name: "Jack", age: 18} do
%{name: "PB"} -> "You are PB!, 99% off!"
%{level: level} -> "#{level * 10}% off!"
_ -> "Error"
end
這邊是上一篇練習的 case 版本,同樣的,如果有要處理上面條件都沒有匹配(matching)到的預設值的話,可以在最後一行處理,只是這邊跟剛剛的 cond 不同,是使用 _
來讓他一定匹配成功,如果有需要左邊的值的話,可以使用變數匹配取出。
我們也可以替 pattern matching 的匹配判斷加上額外的要求,
我們沿用上面的折扣例子,
case %{name: "Jack", age: 18} do
%{name: "PB"} ->
"You are PB!, 99% off!"
%{level: level} when level > 0 && level <= 10 ->
"#{level * 10}% off!"
_ ->
"Error"
end
我們在匹配 level 那行增加了額外的條件,level 必須大於 0 且小於等於 10
這個行為在函式呼叫也可以使用
def take_file(i) when is_integer(i) do
"i 一定是 Integer 才會進入這個匹配結果"
end
def take_file(i) do
"別的結果"
end